/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2024-07-03     huger       the first version
 */

#include "bsp.h"



extern NOR_HandleTypeDef hnor1;


void MPU_Config(void)
{
    MPU_Region_InitTypeDef MPU_InitStruct;

    // 禁用MPU
    HAL_MPU_Disable();

    // 配置FMC区域
    MPU_InitStruct.Enable = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress = Bank1_SRAM1_ADDR;  // 这是FMC NE1的基地址，确保与实际配置匹配
    MPU_InitStruct.Size = MPU_REGION_SIZE_256MB;  // 根据实际的FMC区域大小设置
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;  // 不缓冲
    MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;    // 不缓存
    MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;        // 可共享
    MPU_InitStruct.Number = MPU_REGION_NUMBER0;  // 使用的MPU区域编号
    MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;  // 设置为Strongly Ordered
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);

    // 启用MPU
    HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}

/* FSMC initialization function */
void MX_FSMC_Init(void)
{

  /* USER CODE BEGIN FSMC_Init 0 */

  /* USER CODE END FSMC_Init 0 */

  FSMC_NORSRAM_TimingTypeDef Timing = {0};

  /* USER CODE BEGIN FSMC_Init 1 */

  /* USER CODE END FSMC_Init 1 */

  /** Perform the NOR1 memory initialization sequence
  */
  hnor1.Instance = FSMC_NORSRAM_DEVICE;
  hnor1.Extended = FSMC_NORSRAM_EXTENDED_DEVICE;
  /* hnor1.Init */
  hnor1.Init.NSBank = FSMC_NORSRAM_BANK1;
  hnor1.Init.DataAddressMux = FSMC_DATA_ADDRESS_MUX_ENABLE;
  hnor1.Init.MemoryType = FSMC_MEMORY_TYPE_NOR;
  hnor1.Init.MemoryDataWidth = FSMC_NORSRAM_MEM_BUS_WIDTH_16;
  hnor1.Init.BurstAccessMode = FSMC_BURST_ACCESS_MODE_DISABLE;
  hnor1.Init.WaitSignalPolarity = FSMC_WAIT_SIGNAL_POLARITY_LOW;
  hnor1.Init.WrapMode = FSMC_WRAP_MODE_DISABLE;
  hnor1.Init.WaitSignalActive = FSMC_WAIT_TIMING_BEFORE_WS;
  hnor1.Init.WriteOperation = FSMC_WRITE_OPERATION_ENABLE;
  hnor1.Init.WaitSignal = FSMC_WAIT_SIGNAL_DISABLE;
  hnor1.Init.ExtendedMode = FSMC_EXTENDED_MODE_DISABLE;
  hnor1.Init.AsynchronousWait = FSMC_ASYNCHRONOUS_WAIT_DISABLE;
  hnor1.Init.WriteBurst = FSMC_WRITE_BURST_DISABLE;
  hnor1.Init.PageSize = FSMC_PAGE_SIZE_NONE;
  /* Timing */
  Timing.AddressSetupTime = 3;  // /* 地址建立时间，min(LAN9252)=10ns=>3*5.95238=17.7ns，3个FMC时钟周期 */
  Timing.AddressHoldTime = 3;  /* 地址保持时间，min(LAN9252)=5ns=>2*5.95238=11.8ns，模式A时用不到此参数 */
  Timing.DataSetupTime = 3;    /* 数据保持时间，min(LAN9252)=5ns=>2*5.95238=11.8ns，2个FMC时钟周期 */
  Timing.BusTurnAroundDuration = 0;
  Timing.CLKDivision = 0;
  Timing.DataLatency = 0;
  Timing.AccessMode = FSMC_ACCESS_MODE_A;
  /* ExtTiming */

  if (HAL_NOR_Init(&hnor1, &Timing, NULL) != HAL_OK)
  {
    Error_Handler( );
  }

  /* USER CODE BEGIN FSMC_Init 2 */

  /* USER CODE END FSMC_Init 2 */
}


/*********************************************************************************************************
* Function Name  : Exit_IRQ_Init
* Description    : PE3外部中断初始化，下降沿触发。对应lan9252的 IRQ 中断
* Input          : None
* Output         : None
* Return         : None
* Attention      : None
*********************************************************************************************************/
void Exit_IRQ_Init(void)   //irq中断
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    /* GPIO Ports Clock Enable */
    __HAL_RCC_GPIOE_CLK_ENABLE();

    /*Configure GPIO pins : PE3*/
    GPIO_InitStruct.Pin = GPIO_PIN_3;
    GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

    /* EXTI interrupt init*/
    HAL_NVIC_SetPriority(EXTI3_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(EXTI3_IRQn);
}
/*********************************************************************************************************
* Function Name  : Exit_SYNC0_Init
* Description    : PE4外部中断初始化，下降沿触发。对应lan9252的 SYNC0 中断
* Input          : None
* Output         : None
* Return         : None
* Attention      : None
*********************************************************************************************************/
void Exit_SYNC0_Init(void)   //sync0 中断
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    /* GPIO Ports Clock Enable */
    __HAL_RCC_GPIOE_CLK_ENABLE();

    /*Configure GPIO pins : PE4*/
    GPIO_InitStruct.Pin = GPIO_PIN_4;
    GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

    /* EXTI interrupt init*/
    HAL_NVIC_SetPriority(EXTI4_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(EXTI4_IRQn);
}

/*********************************************************************************************************
* Function Name  : Exit_SYNC1_Init
* Description    : PE5外部中断初始化，下降沿触发。对应lan9252的 SYNC1 中断
* Input          : None
* Output         : None
* Return         : None
* Attention      : None
*********************************************************************************************************/
void Exit_SYNC1_Init(void)   //sync1 中断
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    /* GPIO Ports Clock Enable */
    __HAL_RCC_GPIOE_CLK_ENABLE();

    /*Configure GPIO pins : PE5*/
    GPIO_InitStruct.Pin = GPIO_PIN_5;
    GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

    /* EXTI interrupt init*/
    HAL_NVIC_SetPriority(EXTI9_5_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);
}

u32 PMPReadDWord (u16 Address)
{
    U32 res;
    res.word[0] = *(__IO uint16_t*) (Bank1_SRAM1_ADDR + Address);
    Address +=2;
    res.word[1] = *(__IO uint16_t*) (Bank1_SRAM1_ADDR + Address);
    return res.dword;
}

void PMPWriteDWord (u16 Address, u32 Val)
{
    U32 res;
    res.dword=Val;
    *(__IO uint16_t*) (Bank1_SRAM1_ADDR + Address) = res.word[0];
    Address +=2;
    *(__IO uint16_t*) (Bank1_SRAM1_ADDR + Address) = res.word[1];
}

void PMPReadRegUsingCSR(u8 *ReadBuffer, u16 Address, u8 Count)
{
    U32 param32_1 = {0};
    u8 i = 0;
    param32_1.word[0] = Address;
    param32_1.byte[2] = Count;
    param32_1.byte[3] = ESC_READ_BYTE;
    PMPWriteDWord (CSR_CMD_REG, param32_1.dword);
    do
    {
        param32_1.dword = PMPReadDWord (ESC_CSR_CMD_REG);
    }while(param32_1.byte[3] & ESC_CSR_BUSY);

    param32_1.dword = PMPReadDWord (ESC_CSR_DATA_REG);
    for(i=0;i<Count;i++) ReadBuffer[i] = param32_1.byte[i];
    return;
}

void PMPWriteRegUsingCSR( u8 *WriteBuffer, u16 Address, u8 Count)
{
    U32 param32_1 = {0};
    u8 i = 0;

    for(i=0;i<Count;i++)    param32_1.byte[i] = WriteBuffer[i];
    PMPWriteDWord (ESC_CSR_DATA_REG, param32_1.dword);
    param32_1.word[0] = Address;
    param32_1.byte[2] = Count;
    param32_1.byte[3] = ESC_WRITE_BYTE;
    PMPWriteDWord (0x304, param32_1.dword);
    do
    {
        param32_1.dword = PMPReadDWord (0x304);
    }while(param32_1.byte[3] & ESC_CSR_BUSY);
    return;
}

void PMPReadPDRamRegister(u8 *ReadBuffer, u16 Address, u16 Count)
{
    U32 param32_1 = {0};
    u8 i = 0,nlength, nBytePosition;
    u8 nReadSpaceAvblCount;

    /*Reset/Abort any previous commands.*/
    param32_1.dword = PRAM_RW_ABORT_MASK;
    PMPWriteDWord (PRAM_READ_CMD_REG, param32_1.dword);

    /*The host should not modify this field unless the PRAM Read Busy
    (PRAM_READ_BUSY) bit is a 0.*/
    do
    {
        param32_1.dword = PMPReadDWord (PRAM_READ_CMD_REG);
    }while((param32_1.byte[3] & PRAM_RW_BUSY_8B));

    /*Write address and length in the EtherCAT Process RAM Read Address and
    * Length Register (ECAT_PRAM_RD_ADDR_LEN)*/
    param32_1.word[0] = Address;
    param32_1.word[1] = Count;
    PMPWriteDWord (PRAM_READ_ADDR_LEN_REG, param32_1.dword);
    param32_1.dword = PMPReadDWord (HBI_INDEXED_DATA2_REG );
    /*Set PRAM Read Busy (PRAM_READ_BUSY) bit(-EtherCAT Process RAM Read Command Register)
    *  to start read operatrion*/
    param32_1.dword = PRAM_RW_BUSY_32B;
    PMPWriteDWord (PRAM_READ_CMD_REG, param32_1.dword);
    /*Read PRAM Read Data Available (PRAM_READ_AVAIL) bit is set*/
    do
    {
        param32_1.dword = PMPReadDWord (PRAM_READ_CMD_REG);
    }while(!(param32_1.byte[0] & IS_PRAM_SPACE_AVBL_MASK));

    nReadSpaceAvblCount = param32_1.byte[1] & PRAM_SPACE_AVBL_COUNT_MASK;
    /*Fifo registers are aliased address. In indexed it will read indexed data reg 0x04, but it will point to reg 0
     In other modes read 0x04 FIFO register since all registers are aliased*/
    param32_1.dword = PMPReadDWord (PRAM_READ_FIFO_REG);

    nReadSpaceAvblCount--;
    nBytePosition = (Address & 0x03);
    nlength = (4-nBytePosition) > Count ? Count:(4-nBytePosition);
    memcpy(ReadBuffer+i ,&param32_1.byte[nBytePosition],nlength);
    Count-=nlength;
    i+=nlength;

    while(Count && nReadSpaceAvblCount)
    {
        param32_1.dword = PMPReadDWord (PRAM_READ_FIFO_REG);
        nlength = Count > 4 ? 4: Count;
        memcpy((ReadBuffer+i) ,&param32_1,nlength);
        i+=nlength;
        Count-=nlength;
        nReadSpaceAvblCount --;
        if (!nReadSpaceAvblCount)
        {
            param32_1.dword = PMPReadDWord (PRAM_READ_CMD_REG);
            nReadSpaceAvblCount = param32_1.byte[1] & PRAM_SPACE_AVBL_COUNT_MASK;
        }
    }
    return;
}

void PMPWritePDRamRegister(u8 *WriteBuffer, u16 Address, u16 Count)
{
    U32 param32_1 = {0};
    u8 i = 0,nlength, nBytePosition,nWrtSpcAvlCount;

    /*Reset or Abort any previous commands.*/
    param32_1.dword = PRAM_RW_ABORT_MASK;                                                 /*TODO:replace with #defines*/
    PMPWriteDWord (PRAM_WRITE_CMD_REG, param32_1.dword);
    /*Make sure there is no previous write is pending
    (PRAM Write Busy) bit is a 0 */
    do
    {
        param32_1.dword = PMPReadDWord (PRAM_WRITE_CMD_REG);
    }while((param32_1.byte[3] & PRAM_RW_BUSY_8B));

    /*Write Address and Length Register (ECAT_PRAM_WR_ADDR_LEN) with the
    starting byte address and length)*/
    param32_1.word[0] = Address;
    param32_1.word[1] = Count;

    PMPWriteDWord (PRAM_WRITE_ADDR_LEN_REG, param32_1.dword);
    /*write to the EtherCAT Process RAM Write Command Register (ECAT_PRAM_WR_CMD) with the  PRAM Write Busy
    (PRAM_WRITE_BUSY) bit set*/

    param32_1.dword = PRAM_RW_BUSY_32B;
    PMPWriteDWord (PRAM_WRITE_CMD_REG, param32_1.dword);
    /*Read PRAM write Data Available (PRAM_READ_AVAIL) bit is set*/
    do
    {
        param32_1.dword = PMPReadDWord (PRAM_WRITE_CMD_REG);
    }while(!(param32_1.byte[0] & IS_PRAM_SPACE_AVBL_MASK));

    /*Check write data available count*/
    nWrtSpcAvlCount = param32_1.byte[1] & PRAM_SPACE_AVBL_COUNT_MASK;

    /*Write data to Write FIFO) */
    /*get the byte lenth for first read*/
    nBytePosition = (Address & 0x03);

    nlength = (4-nBytePosition) > Count ? Count:(4-nBytePosition);

    param32_1.dword = 0;
    memcpy(&param32_1.byte[nBytePosition],WriteBuffer+i, nlength);
    PMPWriteDWord (PRAM_WRITE_FIFO_REG,param32_1.dword);
    nWrtSpcAvlCount--;
    Count-=nlength;
    i+=nlength;

    while(nWrtSpcAvlCount && Count)
    {
        nlength = Count > 4 ? 4: Count;
        param32_1.dword = 0;
        memcpy(&param32_1, (WriteBuffer+i), nlength);

        PMPWriteDWord (PRAM_WRITE_FIFO_REG,param32_1.dword);

        i+=nlength;
        Count-=nlength;
        nWrtSpcAvlCount--;

        if (!nWrtSpcAvlCount)
        {
            param32_1.dword = PMPReadDWord (PRAM_WRITE_CMD_REG);
            /*Check write data available count*/
            nWrtSpcAvlCount = param32_1.byte[1] & PRAM_SPACE_AVBL_COUNT_MASK;
        }
    }
    return;
}
void PMPReadDRegister(u8 *ReadBuffer, u16 Address, u16 Count)
{
    if (Address >= 0x1000)
    {
         PMPReadPDRamRegister(ReadBuffer, Address,Count);
    }
    else
    {
         PMPReadRegUsingCSR(ReadBuffer, Address,Count);
    }
}
void PMPWriteRegister( u8 *WriteBuffer, u16 Address, u16 Count)
{
   if (Address >= 0x1000)
   {
        PMPWritePDRamRegister(WriteBuffer, Address,Count);
   }
   else
   {
        PMPWriteRegUsingCSR(WriteBuffer, Address,Count);
   }
}


